package com.powergisol.core.math;



public class Dot {

    private double x;

    private double y;

    private double z;

    public Dot(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    /**
     * 求点到点的距离
     * @param dot
     * @return
     */
    public double distanceto(Dot dot) {
        double dis = Math.pow(this.getX() - dot.getX(), 2) + Math.pow(this.getY() - dot.getY(), 2) + Math.pow(this.getZ(),dot.getZ());
        dis = Math.sqrt(dis);
        return dis;
    }

    /**
     * https://wenku.baidu.com/view/ed88f4bcfab069dc51220129.html
     * 求点到直线段的最短距离
     * @param line
     * @return
     */
    public double distanceto(Line line) {

        /**
         * 设点过点到直线的垂点为 C(x y z) 已知点的位置为 M(x0,y0,z0),直线经过点 B (x1,y1,z1)
         * 直线方向向量为(m,n,q)
         * 直线的点向式为 x-x1/m =y-y1/n=z-z1/q=t
         * x= m*t+x1 y=n*t+y1 z= p*t+z1
         * 已知点到垂点的向量垂直于 直线方向向量
         * (x0-x,y0-y,z0-z) 与直线方向向量m,n,q 为0 即 m*(x0-x)+n*(y0-y)+p*(z0-z)=0
         *
         * t=[m*(x0-x1)+n*(y0-y1)+p*(z0-z1)]/(m*m+n*n+p*p)
         * 最后算出垂点(x,y,z),判断垂点C是否在直线AB上
         * 算(x0,y0,z0)到 (x,y,z)的距离
         */

        double x0,y0,z0;
        double x1,y1,z1;
        double m,n,p;
        double t;

        x0 = this.getX();
        y0 = this.getY();
        z0 = this.getZ();

        if(line.getEndpointA()==null || line.getEndpointB()==null){
            throw new RuntimeException("线段的端点不能为空");
        }

        x1 = line.getEndpointA().getX();
        y1 = line.getEndpointA().getY();
        z1 = line.getEndpointA().getZ();

        m = line.getDirectionVector().getX();
        n = line.getDirectionVector().getY();
        p = line.getDirectionVector().getZ();

        t= (m*(x0-x1)+n*(y0-y1)+p*(z0-z1))/(m*m+n*n+p*p);

        double x= m*t+x1 ,y=n*t+y1, z= p*t+z1;

        Dot dotM = new Dot(x0,y0,z0);
        Dot dotC = new Dot(x,y,z);

        double distance;
        //判断点是否在直线线段上，如果不在那么MA或者MB中小的值就是最短距离
        if(line.contains(dotC)){
            distance = dotM.distanceto(dotC);
        }else{
            Dot dotA = new Dot(x1,y1,z1);
            Dot dotB = new Dot(line.getEndpointB().getX(),line.getEndpointB().getY(),line.getEndpointB().getZ());
            distance = Math.min(dotM.distanceto(dotA),dotM.distanceto(dotB));
        }
        return distance;
    }

    /**
     * 点到直线的距离,和线段的区别是不判断端点
     * https://zhidao.baidu.com/question/581480831.html
     * https://blog.csdn.net/zhangSMILE123456/article/details/48711719?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight
     * 找出一个点和一条线间的距离是经常遇见的几何问题之一。假设给出三个点，A，B和C，你想找出点C到点A、B定出的直线间距离。第一步是找出A到B的向量AB和A到C的向量AC，现在我们用该两向量的叉积除以|AB|，这就是我们要找的的距离了（下图中的红线）。
     * (AB X AC)/2是三角形ABC的面积，这个三角形的底是|AB|，高就是C到AB的距离。有时叉积得到的是一个负值，这种情况下距离就是上述结果的绝对值。
     * 当我们要找点到线段的距离时，情况变得稍稍复杂一些。这时线段与点的最短距离可能是点到线段的某一端点，而不是点到直线的垂线。
     * 我们有集中不同的方法来判断这种特殊情况。第一种情况是计算点积AB Bc来判定两线段间夹角。如果点积大于等于零，那么表示AB到BC是在-90到90度间，也就是说C到AB的垂线在AB外，那么AB上到C距离最近的点就是B。
     * 同样，如果BAAC大于等于零，那么点A就是距离C最近的点。如果两者均小于零，那么距离最近的点就在线段AB中的某一点。
     * d = (AB x AC)/|AB|
     * @param line
     * @return
     */
    public double distance(Line line){

        Vector vAB = line.getDirectionVector();
        Dot dotA = line.getM();
        Vector vAC = new Vector(dotA,this);
        double distance = vAB.crossProduct(vAC).module()/(vAB.module());

        return distance;
    }


    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    public double getZ() {
        return z;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Dot dot = (Dot) o;

        if (Double.compare(dot.x, x) != 0) return false;
        if (Double.compare(dot.y, y) != 0) return false;
        return Double.compare(dot.z, z) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(x);
        result = (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(y);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(z);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public String toString() {
        return "Dot{" +
                "x=" + x +
                ", y=" + y +
                ", z=" + z +
                '}';
    }

    public static void main(String[] args) {

        Dot dot = new Dot(1,2,3);

        Line line = new Line(new Dot(1,1,1),new Vector(1,-3,-2));

        double distance = dot.distance(line);
        System.out.println(distance);

    }
}
